// SelectServ.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "stdio.h"
#include "winsock2.h"

#pragma comment(lib,"ws2_32.lib")
#define BUF_SIZE 30
#define SOCK_SIZE 20

void ErrorHandler ( const char* message );

int _tmain ( int argc, _TCHAR* argv[] )
{
	SOCKET servSock, clntSock;
	SOCKADDR_IN servAddr, clntAddr;
	int clntAddrSz;
	int strLen;
	char buf[BUF_SIZE];

	fd_set	fds, copyRead;
	SOCKET socks[SOCK_SIZE];
	TIMEVAL tm;
	int sockNum = 0;
	unsigned long ul = 1;

	WSADATA	wsaData;
	if ( WSAStartup ( MAKEWORD ( 2, 2 ), &wsaData ) == SOCKET_ERROR ) {
		ErrorHandler ( "WSAStartUp Error" );
	}
	servSock = socket ( AF_INET, SOCK_STREAM, 0 );
	if ( servSock == INVALID_SOCKET )
		ErrorHandler ( "socket error" );
	//将socket设置成非阻塞模式
	//ioctlsocket(servSock,FIONBIO,&ul);

	memset ( &servAddr, 0, sizeof ( servAddr ) );
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl ( INADDR_ANY );
	servAddr.sin_port = htons ( atoi ( "8888" ) );

	if ( bind ( servSock, ( const sockaddr* ) &servAddr, sizeof ( servAddr ) ) == SOCKET_ERROR ) {
		ErrorHandler ( "bind error" );
	}

	if ( listen ( servSock, SOCK_SIZE ) == SOCKET_ERROR ) {
		ErrorHandler ( "listen error" );
	}
	//遍历socks，将所有的元素置于无效的socket
	for ( int i = 0; i < SOCK_SIZE; i++ )
		socks[i] = INVALID_SOCKET;
	sockNum += 1;

	socks[0] = servSock;
	FD_ZERO ( &fds );
	FD_SET ( servSock, &fds );
	tm.tv_sec = 100000;
	tm.tv_usec = 0;

	while ( 1 ) {
		copyRead = fds;
		//节点
		int selResult = select ( sockNum, &copyRead, 0, 0, &tm );
		printf ( "select return...\n" );
		if ( selResult == -1 )
			puts ( "select error" );
		else if ( selResult == 0 )
			puts ( "timeout!" );
		else {
			//先判断是否是有新的客户端连接
			//FD_ISSET判断
			if ( FD_ISSET ( socks[0], &copyRead ) ) {
				clntAddrSz = sizeof ( clntAddr );
				clntSock = accept ( servSock, ( SOCKADDR* ) &clntAddr, &clntAddrSz );
				//将socket设置成非阻塞模式
				ioctlsocket ( clntSock, FIONBIO, &ul );
				for ( int i = 0; i < SOCK_SIZE; i++ ) {
					//遍历socks，在元素为无效的socket处插入客户端的socket
					if ( socks[i] == INVALID_SOCKET ) {
						FD_SET ( clntSock, &fds );
						socks[i] = clntSock;
						sockNum++;
						break;
					}
				}
			}
			//遍历所有的客户端socket，0的位置为服务端的socket，所以从1开始
			for ( int i = 1; i < SOCK_SIZE; i++ ) {
				//如果是无效的socket 不必处理
				if ( socks[i] == INVALID_SOCKET ) continue;
				if ( FD_ISSET ( socks[i], &copyRead ) ) {
					strLen = recv ( socks[i], buf, BUF_SIZE, 0 );
					if ( strLen <= 0 ) { //客户端断开了连接
						closesocket ( socks[i] );
						//从fds删除客户端socket
						FD_CLR ( socks[i], &fds );
						//将对应的位置再次置为无效socket
						socks[i] == INVALID_SOCKET;
						sockNum--;
					} else if ( strLen > 0 ) {
						send ( socks[i], buf, strLen, 0 );
					}
				}
			}
		}
	}
	closesocket ( servSock );
	return 0;
}

void ErrorHandler ( const char* message )
{
	fputs ( message, stderr );
	fputc ( '\n', stderr );
	exit ( 1 );
}

//another demo
#include <WinSock2.h>
#include <Windows.h>
#include <MSWSock.h>
#include <stdio.h>
#include <map>
using namespace std;

#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"Mswsock.lib")

int main()
{
	WSAData wsaData;
	if ( 0 != WSAStartup ( MAKEWORD ( 2, 2 ), &wsaData ) ) {
		printf ( "初始化失败!%d\n", WSAGetLastError() );
		Sleep ( 5000 );
		return -1;
	}

	USHORT nport = 6000;
	SOCKET sListen = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP );

	u_long ul = 1;
	ioctlsocket ( sListen, FIONBIO, &ul );

	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons ( nport );
	sin.sin_addr.S_un.S_addr = ADDR_ANY;

	if ( SOCKET_ERROR == bind ( sListen, ( sockaddr* ) &sin, sizeof ( sin ) ) ) {
		printf ( "bind failed!%d\n", WSAGetLastError() );
		Sleep ( 5000 );
		return -1;
	}

	listen ( sListen, 5 );

	//1)初始化一个套接字集合fdSocket，并将监听套接字放入
	fd_set socketSet;
	FD_ZERO ( &socketSet );
	FD_SET ( sListen, &socketSet );

	TIMEVAL time = {1, 0};
	char buf[4096];

	fd_set    readSet;
	FD_ZERO ( &readSet );

	fd_set    writeSet;
	FD_ZERO ( &readSet );
	
	while ( true ) {
		//2）将fdSocket的一个拷贝fdRead传给select函数
		readSet = socketSet;
		writeSet = socketSet;
		//同时检查套接字的可读可写性。
		int   nRetAll = select ( 0, &readSet, &writeSet, NULL, NULL/*&time*/ ); //若不设置超时则select为阻塞
		if ( nRetAll > 0 ) { //-1
			//是否存在客户端的连接请求。
			if ( FD_ISSET ( sListen, &readSet ) ) { //在readset中会返回已经调用过listen的套接字。
				if ( socketSet.fd_count < FD_SETSIZE ) {
					sockaddr_in addrRemote;
					int nAddrLen = sizeof ( addrRemote );
					SOCKET sClient = accept ( sListen, ( sockaddr* ) &addrRemote, &nAddrLen );
					if ( sClient != INVALID_SOCKET ) {
						FD_SET ( sClient, &socketSet );
						printf ( "\n接收到连接：(%s)", inet_ntoa ( addrRemote.sin_addr ) );
					}
				} else {
					printf ( "连接数量已达上限！\n" );
					continue;
				}
			}
			for ( int i = 0; i < socketSet.fd_count; i++ ) {
				if ( FD_ISSET ( socketSet.fd_array[i], &readSet )  ) {
					//调用recv，接收数据。
					int nRecv = recv ( socketSet.fd_array[i], buf, 4096, 0 );
					if ( nRecv > 0 ) {
						buf[nRecv] = 0;
						printf ( "\nrecv  %d :  %s", socketSet.fd_array[i], buf );
					}
				}
				if ( FD_ISSET ( socketSet.fd_array[i], &writeSet )  ) {
					//调用send，发送数据。
					char buf[] = "hello!";
					int nRet = send ( socketSet.fd_array[i], buf, strlen ( buf ) + 1, 0 );
					if ( nRet <= 0 ) {
						if ( GetLastError() == WSAEWOULDBLOCK ) {
							//do nothing
						} else {
							closesocket ( socketSet.fd_array[i] );
							FD_CLR ( socketSet.fd_array[i], &socketSet );
						}
					} else {
						printf ( "\nsend  hello!" );
					}
				}
			}
		} else if ( nRetAll == 0 ) {
			printf ( "time out!\n" );
		} else {
			printf ( "select error!%d\n", WSAGetLastError() );
			Sleep ( 5000 );
			break;
		}
		Sleep ( 1000 );
	}
	closesocket ( sListen );
	WSACleanup();
}



